using System;
using System.Collections;
using System.ComponentModel;
using System.Configuration.Install;
using System.Diagnostics;
using System.DirectoryServices;
using System.IO;
using System.Net;
using System.Security.Principal;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Windows.Forms;
using System.Xml;

using DeploymentExt.CrmSdk30;
using Microsoft.Win32;


namespace DeploymentExt
{
   /// <summary>
   /// Supplementary DLL to assist the installer with deployment
   /// </summary>
   [RunInstaller(true)]
   public class QuoteAdvantageInstaller : System.Configuration.Install.Installer
   {
      /// <summary>
      /// Required designer variable.
      /// </summary>
      private System.ComponentModel.Container components = null;

      protected WhoAmIResponse objUserIdentity = null;
      protected string strCrmServiceInputParameter;  // Parameter that was passed into the installer
      protected string strCrmServiceAddress;
      protected string strCrm30ServiceUrl;
      protected string strCrm40ServiceUrl;
      protected Crm40Installer objCrm40Installer = new Crm40Installer();
      protected int nMajorCrmVersion = 0;
      protected int nMinorCrmVersion = 0;
      protected string strOrganizationName;
      protected MessageWindow msg = new MessageWindow();
      protected string strEmailNotice = "\r\n\r\nIf you need additional assistance, please send email to support@errorfreeorders.com\r\n";

      public QuoteAdvantageInstaller()
      {
         // This call is required by the Designer.
         InitializeComponent();
      }

      /// <summary> 
      /// Clean up any resources being used.
      /// </summary>
      protected override void Dispose(bool disposing)
      {
         if (disposing)
         {
            if (components != null)
            {
               components.Dispose();
            }
         }
         base.Dispose(disposing);
      }


      public override void Install(IDictionary savedState)
      {
         try
         {
            base.Install(savedState);
            string strQuoteAdvantageFolder = this.Context.Parameters["TARGETDIR"];

            strCrmServiceInputParameter = this.Context.Parameters["CRM_SERVER_URL"];

            if (strQuoteAdvantageFolder[strQuoteAdvantageFolder.Length - 1] != '\\')
               strQuoteAdvantageFolder += "\\"; // Make sure the path ends with a trailing slash

            RegistryKey objKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\MSCRM");
            string strCrmFolder = objKey.GetValue("WebSitePath").ToString();
            string strQuoteAdvantageWebFolder = strCrmFolder + "\\QuoteAdvantageWeb\\";

            if (MicrosoftCrmIsAvailable())
            {
               if (this.nMajorCrmVersion > 3)
               {
                  // Microsoft CRM 4.0 Installation
                  objCrm40Installer.PerformInstallation(strCrm40ServiceUrl, strOrganizationName);
                  AdjustWebConfig(strQuoteAdvantageWebFolder, strCrm40ServiceUrl);
                  CopyDataFiles(strQuoteAdvantageWebFolder, strQuoteAdvantageFolder);
                  CreateVirtualDirectory(strQuoteAdvantageWebFolder);
                  RestartIIS();
               }
               else
               {
                  throw new ApplicationException("Microsoft CRM version 4.0 or newer is required. The QuoteAdvantage installation was not successful.");
               }
            }
            else
            {
               throw new ApplicationException("Microsoft CRM was not available. The QuoteAdvantage installation was not successful.");
            }
         }
         catch (Exception ex)
         {
            //     string strMessage = "Installation Failure: " + ex.Message;
            //     MessageBox.Show(strMessage);
            throw ex;
         }
      }

      // If there are no QuoteAdvantage XML files in the bin folder, then copy in some new ones
      private void CopyDataFiles(string strQuoteAdvantageWebFolder, string strQuoteAdvantageFolder)
      {
         string strSamplesFolder = strQuoteAdvantageFolder + "Sample Data\\Bicycle\\";
         string strBinFolder = strQuoteAdvantageWebFolder + "\\bin\\";

         if (File.Exists(strBinFolder + "CategoryList.xml") == false)
         {
            File.Copy(strSamplesFolder + "CategoryList.xml", strBinFolder + "CategoryList.xml");
         }

         if (File.Exists(strBinFolder + "BicycleRules.xml") == false)
         {
            File.Copy(strSamplesFolder + "BicycleRules.xml", strBinFolder + "BicycleRules.xml");
         }
      }

      // Adjust the QuoteAdvantage web.config file
      private void AdjustWebConfig(string strQuoteAdvantageWebFolder, string strWebServiceUrl)
      {
         try
         {
            string strDiscoveryDomain = "MY_DOMAIN";
            string strDiscoveryUser = "MY_USER";
            string strDiscoveryPassword = "MY_PASSWORD";

            // Load the web.config file
            string strWebConfigPath = strQuoteAdvantageWebFolder + "\\web.config";
            XmlDocument docXml = new XmlDocument();
            docXml.Load(strWebConfigPath);

            XmlNode nodeRoot = docXml.DocumentElement;
            XmlNode nodeAppSettings = docXml.SelectSingleNode("/configuration/appSettings");

            if (nodeAppSettings == null)
            {
               MessageBox.Show("Unable to load <appSettings> node from web.config file");
            }
            else
            {     
               UpdateAppSetting("CrmDiscovery_Domain", strDiscoveryDomain, docXml, nodeAppSettings);
               UpdateAppSetting("CrmDiscovery_User", strDiscoveryUser, docXml, nodeAppSettings);
               UpdateAppSetting("CrmDiscovery_Password", strDiscoveryPassword, docXml, nodeAppSettings);
            }

            // Adjust .NET 2.0 settings
            XmlNode nodeCrmService = docXml.SelectSingleNode("/configuration/applicationSettings/QuoteAdvantage.Properties.Settings/setting[@name='QuoteAdvantage_CrmSdk_CrmService']/value");
            nodeCrmService.InnerText = strWebServiceUrl;

            string strDiscoveryServiceUrl = strWebServiceUrl.Replace("CrmService.asmx", "AD/CrmDiscoveryService.asmx");
            XmlNode nodeDiscoveryService = docXml.SelectSingleNode("/configuration/applicationSettings/QuoteAdvantage.Properties.Settings/setting[@name='QuoteAdvantage_CrmSdk_Discovery_CrmDiscoveryService']/value");
            nodeDiscoveryService.InnerText = strDiscoveryServiceUrl;

            docXml.Save(strWebConfigPath);
         }
         catch (Exception ex)
         {
            MessageBox.Show("Unable to adjust web.config file: " + ex.Message + strEmailNotice);
         }
      }

      private static void UpdateAppSetting(string strKeyName, string strValue, XmlDocument docXml, XmlNode nodeAppSettings)
      {
         // Adjust the security settings for the CRM Discovery Service
         XmlNode nodeDiscoveryDomain = nodeAppSettings.SelectSingleNode("add[@key='" + strKeyName + "']");

         if (nodeDiscoveryDomain == null)
         {
            nodeDiscoveryDomain = docXml.CreateElement("", "add", "");
            XmlAttribute objKeyAttrib = docXml.CreateAttribute("", "key", "");
            objKeyAttrib.Value = "CrmDiscovery_Domain";

            XmlAttribute objValueAttrib = docXml.CreateAttribute("", "value", "");
            objValueAttrib.Value = strValue;

            nodeDiscoveryDomain.Attributes.Append(objKeyAttrib);
            nodeDiscoveryDomain.Attributes.Append(objValueAttrib);

            nodeAppSettings.AppendChild(nodeDiscoveryDomain);
         }
         else
         {
            nodeDiscoveryDomain.Attributes["value"].Value = strValue;
         }
      }

      private string FindCrmWebAddress()
      {
         if (strCrmServiceInputParameter == "")
         {
            // Locate the CRM web service using the registry
            RegistryKey objKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\MSCRM");

            if (objKey != null)
            {
               object objRegistryValue = objKey.GetValue("ServerUrl");

               if (objRegistryValue != null)
               {
                  strCrmServiceAddress = objRegistryValue.ToString();
               }
            }
         }
         else
         {
            // Use the parameter that was passed in from the installer GUI
            strCrmServiceAddress = strCrmServiceInputParameter;
         }

         strCrm30ServiceUrl = strCrmServiceAddress + "/2006/CrmService.asmx";
         strCrm40ServiceUrl = strCrmServiceAddress + "/2007/CrmService.asmx";

         return strCrm30ServiceUrl;
      }

      private bool MicrosoftCrmIsAvailable()
      {
         bool bResult = false;
         bool bDone = false;

         while (!bDone)
         {
            bResult = VerifyCrmWebService();

            if (bResult)
            {
               bDone = true;  // We successfully connected to the 3.0 web service
               GetCrmVersion();
            }
            else
            {
               DialogResult dlgResult = MessageBox.Show("The Microsoft CRM Web Service is not available. Please make sure that Microsoft CRM is running on this server.", "QuoteAdvantage Installation", MessageBoxButtons.RetryCancel);
               if (dlgResult == DialogResult.Cancel)
               {
                  bDone = true;  // We're giving up
               }
            }
         }

         return bResult;
      }

      private bool GetCrmVersion()
      {
         bool bResult = true;
         Crm40Installer objInstaller = new Crm40Installer();

         strOrganizationName = GetOrganizationName();
         bResult = objInstaller.GetCrmVersion(strCrm40ServiceUrl, strOrganizationName);

         if (bResult)
         {
            nMajorCrmVersion = objInstaller.MajorCrmVersion;
            nMinorCrmVersion = objInstaller.MinorCrmVersion;
         }
         else
         {
            // If the Microsoft CRM 4.0 web APIs do not respond then assume that this is version 3.0
            nMajorCrmVersion = 3;
            nMinorCrmVersion = 0;
            bResult = true;
         }

         return bResult;
      }

      private string GetOrganizationName()
      {
         string strName = "";

         try
         {
            //MessageBox.Show("Retrieving Organization Name from: " + strCrm30ServiceUrl);
            CrmService objService = new CrmService();

            objService.Url = strCrm30ServiceUrl;
            objService.Credentials = objCrm40Installer.GetCredentials(strCrm30ServiceUrl);
            TargetRetrieveBusinessUnit targetGetBusinessUnit = new TargetRetrieveBusinessUnit();
            targetGetBusinessUnit.EntityId = objUserIdentity.BusinessUnitId;
            RetrieveRequest getBusinessUnit = new RetrieveRequest();
            getBusinessUnit.Target = targetGetBusinessUnit;
            getBusinessUnit.ColumnSet = new AllColumns();
            RetrieveResponse businessUnitResponse = (RetrieveResponse)objService.Execute(getBusinessUnit);
            businessunit objBusinessUnit = (businessunit)businessUnitResponse.BusinessEntity;

            ColumnSet objColumns = new ColumnSet();
            objColumns.Attributes = new string[] { "currencyformatcode", "currencysymbol", "organizationid", "name" };
            organization objCrmOrganization = (organization)objService.Retrieve(EntityName.organization.ToString(), objBusinessUnit.organizationid.Value, objColumns);

            if (objCrmOrganization != null)
            {
               strName = objCrmOrganization.name;
            }

            //MessageBox.Show("Organization Name: " + strName);
         }
         catch (Exception ex)
         {
            MessageBox.Show("Error while retrieving CRM Organization Name: " + ex.Message);
         }

         return strName;
      }

      private void ShowMessage(string strMessage)
      {
         msg.Message = strMessage;
         msg.Show();
         msg.BringToFront();
         msg.Update();
      }

      private void HideMessage()
      {
         msg.Hide();
      }

      // Check to make sure that we can talk to Microsoft CRM
      private bool VerifyCrmWebService()
      {
         ShowMessage("Connecting to Microsoft CRM");
         bool bResult = true;
         CrmService objService = new CrmService();

         try
         {
            FindCrmWebAddress();
            objService.Url = strCrm30ServiceUrl;
            objService.Credentials = objCrm40Installer.GetCredentials(strCrm30ServiceUrl);

            // Create the request object.
            WhoAmIRequest userRequest = new WhoAmIRequest();

            // Execute the request.
            objUserIdentity = (WhoAmIResponse)objService.Execute(userRequest);
         }
         catch (Exception ex)
         {
            MessageBox.Show("Unable to establish communication with the Microsoft CRM Web Service:\r\n" + ex.Message + strEmailNotice);
            bResult = false;
         }

         HideMessage();

         return bResult;
      }

      public void RestartIIS()
      {
         RestartWebServerForm frmRestart = new RestartWebServerForm();

         frmRestart.ShowDialog();
         if (frmRestart.Result == true)
         {
            try
            {
               ShowMessage("Please wait. Restarting IIS...");
               Process objIISReset = new Process();
               objIISReset.StartInfo.FileName = "iisreset.exe";
               objIISReset.StartInfo.CreateNoWindow = true;
               objIISReset.StartInfo.RedirectStandardOutput = true;
               objIISReset.StartInfo.UseShellExecute = false;
               objIISReset.Start();
               objIISReset.WaitForExit();
            }
            catch (Exception ex)
            {
               MessageBox.Show("Error restarting IIS: " + ex.Message);
            }
         }
      }

      public void CreateVirtualDirectory(string strQuoteAdvantageWebFolder)
      {
         try
         {
            RegistryKey objKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\MSCRM");
            string strWebsiteName = objKey.GetValue("website").ToString();
            string[] objPathList = strWebsiteName.Split('/');
            string strWebServerNumber = objPathList[objPathList.Length - 1];
            int nWebServerNumber = Convert.ToInt32(strWebServerNumber);

            CreateNewVirtualDirectory(nWebServerNumber, "QuoteAdvantageWeb", strQuoteAdvantageWebFolder, true);
         }
         catch (Exception ex)
         {
            MessageBox.Show("Error creating virtual directory: " + ex.Message);
         }
      }

      public void CreateNewVirtualDirectory(int ServerId, string VirtualDirName, string Path, bool AccessScript)
      {
         DirectoryEntry Parent = new DirectoryEntry(@"IIS://localhost/W3SVC/" + ServerId.ToString() + "/Root");
         DirectoryEntry NewVirtualDir;
         bool bFound = false;

         // Check for existing entry
         foreach (DirectoryEntry entry in Parent.Children)
         {
            if (entry.Name == VirtualDirName)
               bFound = true;
         }

         if (!bFound)
         {
            NewVirtualDir = Parent.Children.Add(VirtualDirName, "IIsWebVirtualDir");
            NewVirtualDir.Properties["Path"][0] = Path;
            NewVirtualDir.Properties["AccessScript"][0] = AccessScript;
            NewVirtualDir.Properties["AppFriendlyName"][0] = VirtualDirName;
            NewVirtualDir.Properties["AppIsolated"][0] = 2;
            NewVirtualDir.Properties["AppRoot"][0] = "/LM/W3SVC/" + ServerId + "/Root/" + VirtualDirName;
            NewVirtualDir.CommitChanges();
         }
      }


      #region Component Designer generated code
      /// <summary>
      /// Required method for Designer support - do not modify
      /// the contents of this method with the code editor.
      /// </summary>
      private void InitializeComponent()
      {
         components = new System.ComponentModel.Container();
      }
      #endregion
   }
}
